﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;
using SlimMath;

namespace MyTestProject {
    
    public class TestVertexArray {

        [Fact]
        public void TestConstructor () {
            var varry = new VertexArray (4, 3, ArrayType.Float);

            Assert.Equal (4, varry.VertexCount);
            Assert.Equal (3, varry.ComponentCount);
            Assert.Equal (ArrayType.Float, varry.ComponentType);
        }

        [Fact]
        public void TestArrayTypeToByteCount () {
            Assert.Equal (1, ArrayType.Byte.ToByteCount ());
            Assert.Equal (2, ArrayType.Half.ToByteCount ());
            Assert.Equal (2, ArrayType.Short.ToByteCount ());
            Assert.Equal (4, ArrayType.Fixed.ToByteCount ());
            Assert.Equal (4, ArrayType.Float.ToByteCount ());
        }
     
        [Fact]
        public void TestSetByte () {
            var varry = new VertexArray (14, 2, ArrayType.Byte);
            var src = new byte[] {1,255,
                                  2,255,
                                  3,255,
                                  4,255};
            varry.Set (10, 4, src);

            var dst = new byte[8];
            varry.Get (10, 4, dst);

            Assert.Equal (src, dst);
        }

        [Fact]
        public void TestSetSByte () {
            var varry = new VertexArray (14, 2, ArrayType.Byte);
            var src = new sbyte[] {-128, 124,
                                   -128, 125,
                                   -128, 126,
                                   -128, 127};
            varry.Set (10, 4, src);

            var dst = new sbyte[8];
            varry.Get (10, 4, dst);

            Assert.Equal (src, dst);
        }

        [Fact]
        public void TestSetShort () {
            var varry = new VertexArray (14, 1, ArrayType.Short);
            var src = new short[] { 0, 
                                    1,
                                   -32728,
                                    32727};
            varry.Set (10, 4, src);
            
            var  dst = new short[4];
            varry.Get (10, 4, dst);

            Assert.Equal (src, dst);
        }

        [Fact]
        public void TestSetUShort () {
            var varry = new VertexArray (14, 1, ArrayType.Short);
            var src = new ushort[] { 0,
                                     1,
                                     2,
                                     65535};
            varry.Set (10, 4, src);

            var  dst = new ushort[4];
            varry.Get (10, 4, dst);

            Assert.Equal (src, dst);
        }

        /*
         * half --> (half) --> half
         * (基本half型にはushort型を使うのが原則でshort型を使うのは反則(だけど一応使える))
         */
        [Fact]
        public void TestSetHalf1 () {
            var varry = new VertexArray (14, 1, ArrayType.Half);
            var src = new short[] { (short)new Half(0).RawValue, 
                                     (short)new Half(1).RawValue,
                                     (short)new Half(-32728).RawValue,
                                     (short)new Half(32727).RawValue};
            varry.Set (10, 4, src);

            var  dst = new short[4];
            varry.Get (10, 4, dst);

            Assert.Equal (src, dst);
        }

        /*
         * half --> (half) --> half
         */
        [Fact]
        public void TestSetHalf2 () {
            var varry = new VertexArray (14, 1, ArrayType.Half);
            var src = new ushort[] { new Half(0).RawValue, 
                                     new Half(1).RawValue,
                                     new Half(-32728).RawValue,
                                     new Half(32727).RawValue};
            varry.Set (10, 4, src);

            var  dst = new ushort[4];
            varry.Get (10, 4, dst);

            Assert.Equal (src, dst);
        }


        /*
         * float --> (half) --> half
         */
        [Fact]
        public void TestSetHalf3 () {
            var varry = new VertexArray (14, 1, ArrayType.Half);
            var src = new float[] { 0f,
                                    1f,
                                    -32728f,
                                    32727f};
            varry.Set (10, 4, src);

            var  dst = new ushort[4];
            varry.Get (10, 4, dst);

            var exptected = new ushort[] { new Half(0).RawValue, 
                                           new Half(1).RawValue,
                                           new Half(-32728).RawValue,
                                           new Half(32727).RawValue};
           
            Assert.Equal (exptected, dst);
        }

        [Fact]
        public void TestSetFixed () {
            var varry = new VertexArray (1, 4, ArrayType.Fixed);
            var src = new int[] { 0, 0, 0x12345678, -0x12345678 };
            varry.Set (0, 1, src);

            var dst = new int[4];
            varry.Get (0, 1, dst);

            Assert.Equal (src, dst);
        }


        [Fact]
        public void TestSetUFixed () {
            var varry = new VertexArray (1, 4, ArrayType.Fixed);
            var src = new uint[] { 0, 0, 0x87654321, 0x87654321 };
            varry.Set (0, 1, src);

            var dst = new uint[4];
            varry.Get (0, 1, dst);

            Assert.Equal (src, dst);
        }

        [Fact]
        public void TestSetFloat () {
            var varry = new VertexArray (2, 4, ArrayType.Float);
            var src = new float[] {1,2,3,4,
                                   5,6,7,8};
            varry.Set (0, 2, src);
           
            var dst = new float[8];
            varry.Get (0, 2, dst);

            Assert.Equal (src, dst);
        }

        [Fact]
        public void TestSetVertexArray () {
            var varry1 = new VertexArray (4, 3, ArrayType.Float);
            var varry2 = new VertexArray (4, 3, ArrayType.Float);
            var src = new float[] {1,2,3,
                                   4,5,6,
                                   7,8,9,
                                   10,11,12};
            varry1.Set (0, 4, src);
            varry2.Set (2, 2, varry1);

            var dst = new float[12];
            varry2.Get (0, 4, dst);

            var expected = new float[]{0,0,0,
                                       0,0,0,
                                       1,2,3,
                                       4,5,6};

            Assert.Equal(expected, dst);
        }

        [Fact]
        public void TestGetByteAsFloat () {
            var varry = new VertexArray (4, 2, ArrayType.Byte);
            var src = new byte[] {1, 255,
                                  2, 255,
                                  3, 255,
                                  4, 255};
            varry.Set (0, 4, src);

            var expected = new float[] {1f, 255f,
                                        2f, 255f,
                                        3f, 255f,
                                        4f, 255f};
            var dst = new float[8];
            varry.Get (0, 4, dst);

            Assert.Equal (expected, dst);
        }


        [Fact]
        public void TestGetShortAsFloat () {
            var varry = new VertexArray (4, 2, ArrayType.Short);
            var src = new ushort[] {1, 65535,
                                    2, 65535,
                                    3, 65535,
                                    4, 65535};
            varry.Set (0, 4, src);

            var expected = new float[] {1, 65535,
                                        2, 65535,
                                        3, 65535,
                                        4, 65535};
            var dst = new float[8];
            varry.Get (0, 4, dst);

            Assert.Equal (expected, dst);
        }
        
        /**
         * float --> half(1.5.10) --> float の変換が入るので入れた値と正確に一致するわけではない
         */
        [Fact]
        public void TestGetHalfAsFloat () {
            var varry = new VertexArray (4, 2, ArrayType.Half);
            var src = new float[] {1.0f, 65535.0f,
                                   2.0f, 65535.0f,
                                   3.0f, 65535.0f,
                                   4.0f, 65535.0f};
            varry.Set (0, 4, src);

            // 精度落ちる(65535.0f --> 65504.0f)
            var expected = new float[] {1.0f, 65504.0f,
                                        2.0f, 65504.0f,
                                        3.0f, 65504.0f,
                                        4.0f, 65504.0f};
            var dst = new float[8];
            varry.Get (0, 4, dst);

            Assert.Equal (expected, dst);
        }

    /**
     * float --> fixed(16.16) --> float の変換が入るので入れた値と正確に一致するわけではない
     */
        [Fact]
        public void TestGetFixedAsFloat () {
            var varry = new VertexArray (4, 2, ArrayType.Fixed);
            var src = new uint[] {1<<16, 65535u<<16,
                                  2<<16, 65535u<<16,
                                  3<<16, 65535u<<16,
                                  4<<16, 65535u<<16};
            varry.Set (0, 4, src);

            var expected = new float[] {1, 65535,
                                        2, 65535,
                                        3, 65535,
                                        4, 65535};
            var dst = new float[8];
            varry.Get (0, 4, dst);

            Assert.Equal (expected, dst);
        }

        /**
         * Scale,Biasをかけてfloatで取り出すGetのテスト
         * M3G非標準だがpick()関数内部で使用する
         * */
        [Fact]
        public void TestGetFloatWithScaleBias () {
            var varry = new VertexArray (2, 2, ArrayType.Float);
            var src = new float[] {1, 2, 3, 4};
            varry.Set (0, 2, src);

            var scale = 10f;
            var bias  = new float[] { 10, 10 };

            var expected = new float[] {20, 30, 40, 50};
            var dst = new float[4];
            varry.Get (0, 2, dst, scale, bias);

            Assert.Equal (expected, dst);

        }


        /**
         * 途中のindexにSetしてGetするテスト
         * */
        [Fact]
        public void TestSetIndexAndGetIndex () {
            var varry = new VertexArray (10, 2, ArrayType.Float);
            var src = new float[] {1, 2, 3, 4};
            varry.Set (4, 2, src);

            var expected = new float[] {1, 2, 3, 4};

            var dst = new float[4];
            varry.Get (4, 2, dst);

            Assert.Equal (expected, dst);
        }

        [Fact]
        public void TestDuplicate () {
            var varry = new VertexArray (2, 4, ArrayType.Float);
            var values = new float[] {1,2,3,4, 5,6,7,8};
            varry.Set (0, 2, values);
            varry.UserID = 100;

            var dup = (VertexArray)varry.Duplicate ();

            var srcValues = new float[8];
            var dupValues = new float[8];
            varry.Get (0, 2, srcValues);
            dup.Get (0, 2, dupValues);

            Assert.Equal (varry.VertexCount, dup.VertexCount);
            Assert.Equal (varry.ComponentCount, dup.ComponentCount);
            Assert.Equal (varry.ComponentType, dup.ComponentType);
            Assert.Equal (varry.UserID, dup.UserID);
            Assert.Equal (srcValues, dupValues);
        }

        [Fact]
        public void TestByteArray () {
            var varry = new VertexArray (2, 2, ArrayType.Byte);
            var src = new float[] { 1, 2, 3, 4};
            varry.Set (0, 2, src);

            Assert.Equal (1, varry.ByteArray[0]);
            Assert.Equal (2, varry.ByteArray[1]);
            Assert.Equal (3, varry.ByteArray[2]);
            Assert.Equal (4, varry.ByteArray[3]);
        }

    }
}
